
package com.trydofor.id.body.int64;

import java.util.concurrent.atomic.AtomicInteger;

import com.trydofor.id.byta.Bytable;
import com.trydofor.id.byta.ByteMeta;
import com.trydofor.id.byta.Bytes;

/**
 * reply a long array.
 * 
 * @author : Shi Rongjiu (www.trydofor.com)
 */
public class Int64ReplyBody implements Bytable<Int64ReplyBody> {
    
    private final String        maker;
    private final int           count;
    private final long[]        longs;
    private final AtomicInteger index = new AtomicInteger(-1);
    private final AtomicInteger await = new AtomicInteger(0);
    
    /**
     * make a long array with the fixed size.
     * 
     * @param maker the maker.
     * @param count the count.
     */
    public Int64ReplyBody(String maker, int count) {
        this.maker = maker;
        this.count = count;
        longs = new long[count];
    }
    
    /**
     * append a long value.
     * 
     * @param val the long value to add
     * @return true if appended
     */
    public boolean append(long val) {
        final boolean result;
        await.incrementAndGet(); // doing [+1]
        final int idx = index.incrementAndGet();
        final int max = count - 1;
        if (idx > max) {
            index.set(max);
            result = false;
        }
        else {
            longs[idx] = val; // may exception
            result = true;
        }
        await.decrementAndGet(); // done [-1]
        return result;
    }
    
    /**
     * get a long array of current index.
     * 
     * @return a long array.
     */
    public long[] getLongs() {
        final int count = getDoneCount();
        
        long[] val = new long[count];
        if (count > 0) System.arraycopy(longs, 0, val, 0, count);
        return val;
    }
    
    /**
     * {@inheritDoc}
     */
    public byte[] toBytes() {
        final int array = getDoneCount();
        byte[] mks = Bytes.toMetaBytes(maker);
        byte[] bytes = new byte[mks.length + Bytes.BYTES_INT * 2 + array * Bytes.BYTES_LONG];
        int offset = 0;
        System.arraycopy(mks, 0, bytes, offset, mks.length);
        offset += mks.length;
        Bytes.fill(bytes, offset, count);
        offset += Bytes.BYTES_INT;
        Bytes.fill(bytes, offset, array);
        offset += Bytes.BYTES_INT;
        for (int i = 0; i < array; i++) {
            Bytes.fill(bytes, offset, longs[i]);
            offset += Bytes.BYTES_LONG;
        }
        return bytes;
    }
    
    /**
     * {@inheritDoc}
     */
    public Int64ReplyBody ofBytes(byte[] bs, int offset, int length) {
        ByteMeta<Int64ReplyBody> bm = ofBytes(bs, offset);
        return (bm.byteSize() != length) ? null : bm.instance();
    }
    
    /**
     * {@inheritDoc}
     */
    public ByteMeta<Int64ReplyBody> ofBytes(byte[] bs, int offset) {
        Int64ReplyBody inst = null;
        int size = 0;
        ByteMeta<String> mks = Bytes.ofMetaBytes(bs, offset);
        size += mks.byteSize();
        int count = Bytes.toInt(bs, offset + size, Bytes.BYTES_INT);
        size += Bytes.BYTES_INT;
        int array = Bytes.toInt(bs, offset + size, Bytes.BYTES_INT);
        size += Bytes.BYTES_INT;
        if (bs.length - offset >= array * Bytes.BYTES_LONG + size) {
            inst = new Int64ReplyBody(mks.instance(), count);
            for (int i = 0; i < array; i++) {
                inst.append(Bytes.toLong(bs, offset + size, Bytes.BYTES_LONG));
                size += Bytes.BYTES_LONG;
            }
        }
        
        return ByteMeta.of(inst, size);
    }
    
    /**
     * @return the maker
     */
    public String getMaker() {
        return maker;
    }
    
    /**
     * @return the current size of long array
     */
    public int getSize() {
        return getDoneCount();
    }
    
    /**
     * @return the count
     */
    public int getCount() {
        return count;
    }
    
    // -----------
    private int getDoneCount() {
        int count;
        do { // check
            count = index.get() + 1;
        }
        while (await.get() != 0);
        return count;
    }
}
